//! @file   lc_condition_variable.cpp
//! @brief  
//! @author Sayan Chaliha
//! @date   September, 2011
//!
//! 
#include <lc_condition_variable.h>
#include <lc_system.h>

namespace cyan {
    ConditionVariable::ConditionVariable(Mutex& mutex) : lock_(&mutex) {
        Int count = 0;
        Int returnCode = ::pthread_cond_init(&this->condtionVariable_, nullptr);
        
        while ((returnCode != 0) && (returnCode == EAGAIN ||
                                     returnCode == EBUSY)) {
            returnCode = ::pthread_cond_init(&this->condtionVariable_, nullptr);
            if (returnCode == 0 || ++count == MAX_RETRIES)
                break;
            System::sleep(1);
        }
        
        if (returnCode == 0)
            return;
        
        System::throwSystemException(returnCode);
    }
    
    ConditionVariable::~ConditionVariable() {
        Int count = 0;
        Int returnCode = ::pthread_cond_destroy(&this->condtionVariable_);
        
        while ((returnCode != 0) && (returnCode == EAGAIN ||
                                     returnCode == EBUSY)) {
            this->lock_->lock();
            this->notifyAll();
            this->lock_->unlock();
            
            returnCode = ::pthread_cond_destroy(&this->condtionVariable_);
            if (returnCode == 0 || ++count == MAX_RETRIES)
                break;
            System::sleep(1);
        }
        
        if (returnCode == 0)
            return;
        
        System::throwSystemException(returnCode);
    }
    
    Bool ConditionVariable::wait(const Interval& interval) {
        return this->timedWait_(interval.toSeconds());
    }
    
    void ConditionVariable::notify() {
        Int returnCode = ::pthread_cond_signal(&this->condtionVariable_);
        
        if (returnCode == 0)
            return;
        
        System::throwSystemException(returnCode);
    }
    
    void ConditionVariable::notifyAll() {
        Int returnCode = ::pthread_cond_broadcast(&this->condtionVariable_);
        
        if (returnCode == 0)
            return;
        
        System::throwSystemException(returnCode);
    }
    
    void ConditionVariable::wait_() {
        Int returnCode = ::pthread_cond_wait(&this->condtionVariable_,
                                             &this->lock_->mutex_);
        
        if (returnCode == 0)
            return;
        
        System::throwSystemException(returnCode);
    }
    
    Bool ConditionVariable::timedWait_(Time seconds) {
        ::timeval tv;
        ::timespec ts;

        ::gettimeofday(&tv, nullptr);
        ts.tv_sec = tv.tv_sec + seconds;
        ts.tv_nsec = tv.tv_usec * 1000;

        Int returnCode = ::pthread_cond_timedwait(&this->condtionVariable_,
                                                  &this->lock_->mutex_,
                                                  &ts);

        if (returnCode == 0)
            return true;
        else if (returnCode == ETIMEDOUT)
            return false;
        
        System::throwSystemException(returnCode);
    }
    
    Strobe::Strobe() : isSignalled_(false), conditionVariable_(mutex_) {
    }
    
    Strobe::~Strobe() {
    }
    
    Bool Strobe::wait(const Interval& interval) {
        Bool returnValue = true;
        CriticalSection section(this->mutex_);
        
        if (!this->isSignalled_)
            returnValue = this->conditionVariable_.wait(interval);
        
        return returnValue;
    }
    
    void Strobe::notify() {
        CriticalSection section(this->mutex_);
        
        this->isSignalled_ = true;
        this->conditionVariable_.notify();
    }
    
    void Strobe::notifyAll() {
        CriticalSection section(this->mutex_);
        
        this->isSignalled_ = true;
        this->conditionVariable_.notifyAll();
    }
    
    void Strobe::reset() {
        CriticalSection section(this->mutex_);
        
        this->isSignalled_ = false;
    }
}
